#define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include "matrix.hpp"
#include "vector.hpp"

#include <iostream>
#include <list>
#include <cmath>
#include <regex>
#include <complex>
#include <algorithm>
#include <functional>
#include <appendix/point.h>
#include <parsers/parsers.hpp>

#include "complex.h"

namespace Vector{
	std::vector<double> arg( const std::vector<double>& vecx,const std::vector<double>& vecy )
	{
		if( vecx.size() != vecy.size() )
			throw ( "The size of vectors dismatch!" );
		std::vector<double> res(vecx.size());
		for( int ii=0; ii < res.size(); ii++)
			res[ii] = std::arg(std::complex<double>( vecx[ii],vecy[ii] ));
		return res;
	}

	std::string serialize( const std::vector<double>& vec )
	{
		std::string str = "nl.new_vector({";
		for(auto d:vec){
			str += std::to_string(d) + ";";
		}
		str += "}); ";
		return str;
	}
	std::string serialize( const std::vector<std::complex<double>>& vec )
	{
		std::string str = "nl.new_cvector({";
		for(auto d:vec){
			str += "{" + std::to_string(d.real()) + ";" + std::to_string(d.imag()) + "}; ";
		}
		str += "}); ";
		return str;
	}
	std::string serialize( const std::vector<std::string>& vec )
	{
		std::string str = "nl.new_svector({";
		for(auto d:vec){
			str += "\"" + d + "\";";
		}
		str += "}); ";
		return str;
	}
}
namespace sol {
   template <>
   struct is_automagical<std::vector<std::complex<double> > > : std::false_type {};
   template <>
   struct is_container<std::vector<std::complex<double> > > : std::true_type {};
   template <>
   struct is_to_stringable<std::vector<std::complex<double> > > : std::true_type {};

   template <>
   struct is_automagical<std::vector<Point3Dd > > : std::false_type {};
   template <>
   struct is_automagical<std::vector<Point2Dd > > : std::false_type {};
   template <>
   struct is_automagical<std::vector<Point2Di > > : std::false_type {};
   template <>
   struct is_automagical<std::vector<Point3Di > > : std::false_type {};
}

namespace sol {
   template <>
   struct is_automagical<Point3Dd> : std::false_type {};
   template <>
   struct is_automagical<Point2Dd> : std::false_type {};
   template <>
   struct is_automagical<Point3Di> : std::false_type {};
   template <>
   struct is_automagical<Point2Di> : std::false_type {};
}

template<class T>
sol::object getter(const std::vector<T>& vec, sol::stack_object key, sol::this_state L) {
		// we use stack_object for the arguments because we know
		// the values from Lua will remain on Lua's stack,
		// so long we we don't mess with it
		// String keys failed, check for numbers
		auto maybe_numeric_key = key.as<sol::optional<int>>();
		if (maybe_numeric_key) {
			int n = *maybe_numeric_key;
			if( n <= vec.size() && n >= 1)
				return sol::object(L, sol::in_place, vec[n-1]);
		}
		// No valid key: push nil
		// Note that the example above is a bit unnecessary:
		// binding the variables x and y to the usertype
		// would work just as well and we would not
		// need to look it up here,
		// but this is to show it works regardless
		return sol::object(L, sol::in_place, sol::lua_nil);
}
template<class T,class T2>
void setter(std::vector<T>& vec, int idx, const T2& val) {
	if( idx <= vec.size() && idx >= 1)
		vec[idx-1] = val;
	else
		throw sol::error("out of range");
}

template<class T,class T1>
std::vector<T> operator+(const std::vector<T>& left, const T1& right) {
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]+right;
	}
	return res;
}

template<class T,class T1>
std::vector<T> operator+(const T1& right,const std::vector<T>& left) {
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]+right;
	}
	return res;
}

template<class T>
std::vector<T> operator+(const std::vector<T>& left,const std::vector<T>& right) {
	if( left.size() != right.size() )
		throw sol::error( "The size of vectors dismatch!");
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]+right[ii];
	}
	return res;
}

template<class T,class T1>
std::vector<T> operator-(const std::vector<T>& left, const T1& right) {
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]-right;
	}
	return res;
}
template<class T>
std::vector<T> operator-(const std::vector<T>& left,const std::vector<T>& right) {
	if( left.size() != right.size() )
		throw sol::error( "The size of vectors dismatch!");
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]-right[ii];
	}
	return res;
}

template<class T,class T1>
std::vector<T> operator*(const std::vector<T>& left, const T1& right) {

	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]*right;
	}
	return res;
}

template<class T,class T1>
std::vector<T> operator*(const T1& right,const std::vector<T>& left) {

	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]*right;
	}
	return res;
}
template<class T>
std::vector<T> operator*(const std::vector<T>& left,const std::vector<T>& right) {
	if( left.size() != right.size() )
		throw sol::error( "The size of vectors dismatch!");
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]*right[ii];
	}
	return res;
}
template<class T,class T1>
std::vector<T> operator/(const std::vector<T>& left, const T1& right) {

	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]/right;
	}
	return res;
}

template<class T,class T1>
std::vector<T> operator/(const T1& left,const std::vector<T>& right) {

	std::vector<T> res(right.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left/right[ii];
	}
	return res;
}
template<class T>
std::vector<T> operator/(const std::vector<T>& left,const std::vector<T>& right) {
	if( left.size() != right.size() )
		throw sol::error( "The size of vectors dismatch!");
	std::vector<T> res(left.size());
	for(int ii=0; ii < res.size(); ii++)
	{
		res[ii] = left[ii]/right[ii];
	}
	return res;
}

std::ostream& operator<<(std::ostream& os, const std::complex<double>& right);

template<typename T>
std::string type_name(){
	return "unkown";
}

template <>
std::string type_name< std::vector<double> >()
{
	return "vector";
}

template <>
std::string type_name< std::vector<std::complex<double> > >()
{
	return "complex vector";
}

template <>
std::string type_name< std::vector< int > >()
{
	return "int vector";
}

template <>
std::string type_name< std::vector< bool > >()
{
	return "bool vector";
}

template <>
std::string type_name< std::vector< std::string > >()
{
	return "string vector";
}

template <>
std::string type_name< std::vector<std::vector<double> > >()
{
	return "vvector";
}
template <>
std::string type_name< std::vector<std::vector<int> > >()
{
	return "vivector";
}


template <>
std::string type_name< std::vector<Point3Dd > >()
{
	return "point3D vector";
}

template <>
std::string type_name< std::vector<Point2Dd > >()
{
	return "point2D vector";
}

template <>
std::string type_name< std::vector<Point3Di > >()
{
	return "point3Di vector";
}

template <>
std::string type_name< std::vector<Point2Di > >()
{
	return "point2Di vector";
}

template<class T>
std::ostream& operator<<(std::ostream& os, const std::vector<T>& right) {
	if (right.size() == 0) {
		os << "{ " << type_name<std::vector<T>>() << "! empty }";
		return os;
	}
	auto b = right.begin();
	auto e = right.end();
	os << "{ " << type_name<std::vector<T>>() << " ";
	//os << "{ vector ";
	os << right.size() << " | ";
	os << *b;
	++b;
	while (b != e) {
		os << ", " << *b;
		++b;
	}
	os << " }";
	return os;
}

namespace LuaNumeric {
	typedef std::vector<double> dvector;
	typedef std::vector<int> ivector;
	typedef std::vector<bool> bvector;
	typedef std::vector<std::string> svector;
	typedef std::vector<std::complex<double> > cvector;
	typedef std::complex<double> complex;
	typedef std::vector<Point3Dd > point_vector;
	typedef std::vector<Point2Dd > point2D_vector;
	typedef std::vector<Point3Di > pointi_vector;
	typedef std::vector<Point2Di > point2Di_vector;

	typedef std::vector<dvector> vvector;
	typedef std::vector<ivector> vivector;


	struct lua_iterator_state {
		typedef cvector::iterator it_t;
		it_t it;
		it_t last,begin;
		lua_iterator_state(cvector& mt) : it(mt.begin()), last(mt.end()), begin(mt.begin()) {}
	};

	int my_next1(lua_State* L) {
		// this gets called
		// to start the first iteration, and every
		// iteration there after
		// the state you passed in pairs is argument 1
		// the key value is argument 2
		// we do not care about the key value here
		lua_iterator_state& it_state = sol::stack::get<sol::user<lua_iterator_state>>(L, 1);
		auto& it = it_state.it;
		auto& begin = it_state.begin;
		if (it == it_state.last) {
			return sol::stack::push(L, sol::lua_nil);
		}
		auto itderef = *it;
		// 2 values are returned (pushed onto the stack):
		// the key and the value
		// the state is left alone
		int pushed = sol::stack::push(L, std::distance(begin, it));
		pushed += sol::stack::push_reference(L, itderef);
		std::advance(it, 1);
		return pushed;
	}

	std::tuple<sol::object, sol::object> my_next(sol::user<lua_iterator_state&> user_it_state, sol::this_state l) {
		// this gets called
		// to start the first iteration, and every
		// iteration there after

		// the state you passed in my_pairs is argument 1
		// the key value is argument 2, but we do not
		// care about the key value here
		lua_iterator_state& it_state = user_it_state;
		auto& it = it_state.it;
		auto& begin = it_state.begin;

		if (it == it_state.last) {
			// return nil to signify that
			// there's nothing more to work with.
			return std::make_tuple(
				sol::object(sol::lua_nil),
				sol::object(sol::lua_nil)
			);
		}
		auto itderef = *it;
		// 2 values are returned (pushed onto the stack):
		// the key and the value
		// the state is left alone
		auto r = std::make_tuple(sol::object(l, sol::in_place, 1+std::distance(begin, it)), sol::object(l, sol::in_place, itderef));
		// the iterator must be moved forward one before we return
		std::advance(it, 1);
		return r;
	}

	auto my_pairs(cvector& mt) {
		// pairs expects 3 returns:
		// the "next" function on how to advance,
		// the "table" itself or some state,
		// and an initial key value (can be nil)

		// prepare our state
		lua_iterator_state it_state(mt);
		// sol::user is a space/time optimization over regular usertypes,
		// it's incompatible with regular usertypes and stores the type T directly in lua without any pretty setup
		// saves space allocation and a single dereference
		return std::make_tuple(&my_next, sol::user<lua_iterator_state>(std::move(it_state)), sol::lua_nil);
	}

	template<class T>
	std::string to_string1( const std::vector<T>& data)
	{
		std::ostringstream out;
		out << data ;
		return out.str();
	}

	void open_luaVector(sol::table& module) {

		module.set_function("new_vector", sol::overload(
											[]() { return std::make_shared<dvector>(); }
											,[](int s) { return std::make_shared<dvector>(s); }
											,[](size_t s) { return std::make_shared<dvector>(s); }
											,[](const std::string& str){
											    std::regex reg1("[^0-9.+-eE;,() ]*");
											    std::string t("");
											    std::string str1 = std::regex_replace(str,reg1,t); //trim_left

												auto vec = parsers::parse_numbers_list(str1);
												return std::make_shared<dvector>(vec);
											}
											,[](sol::table t){
												if( auto res = t.as< sol::optional< dvector > > () )
												{
													//logs::debug_log("%d",res->size());
													return std::make_shared<dvector>( *res );
												}
												return std::make_shared<dvector>();
											}
							)
		);
		module.set_function("new_vvector", sol::overload(
											[]() { return std::make_shared<vvector>(); }
											,[](int s) { return std::make_shared<vvector>(s); }
											)
		);
		module.set_function("new_svector", sol::overload(
											[]() { return std::make_shared<svector>(); }
											,[](int s) { return std::make_shared<svector>(s); }
											)
		);
		module.set_function("new_vivector", sol::overload(
											[]() { return std::make_shared<vivector>(); }
											,[](int s) { return std::make_shared<vivector>(s); }
											)
		);
		module.set_function("new_cvector", sol::overload(
												[]() { return std::make_shared<cvector>(); }
												,[](int s) { return std::make_shared<cvector>(s); }
												,[](size_t s) { return std::make_shared<cvector>(s); }
												,[](size_t s,complex d) { return std::make_shared<cvector>(s,d); }
												,[](size_t s,double d) { return std::make_shared<cvector>(s,d); }
												,[](sol::table t){
													auto pts = std::make_shared< cvector >();
													int idx=1;
													while( t[idx].get_type() >  sol::type::nil)
													{
														if( auto pt = GetComplex(t[idx]) )
														{
															pts->push_back( *pt );
														}else
														{
															throw( "Unknown type at ( complex excepted )!" );
														}
														idx++;
													}
													return pts;
												}
		));

		module.set_function("new_ivector", sol::overload(
												[]() { return std::make_shared<ivector>(); }
												,[](int s) { return std::make_shared<ivector>(s); }
												,[](size_t s) { return std::make_shared<ivector>(s); }
												,[](sol::table t){
													if( auto res = t.as< sol::optional< ivector > > () )
													{
														return std::make_shared<ivector>( *res );
													}
													return std::make_shared<ivector>();
												}
		));

		module.set_function("new_ptvector", sol::overload(
														[]() { return std::make_shared<point_vector>(); }
														,[](int s) { return std::make_shared<point_vector>(s); }
														,[](size_t s) { return std::make_shared<point_vector>(s); }
														)
				);
		module.new_usertype<dvector>("vector"
				,"resize", ( void (dvector::*)(size_t)) & dvector::resize
				,"add", sol::overload(
						( void (dvector::*)(const double&)) & dvector::push_back
						,[](dvector& vec, const dvector& vec1){
							vec.insert(vec.end(),vec1.begin(),vec1.end());
						}
				)
				,"sort", sol::overload(
						[](dvector& vec,const std::string& order ){
							if(order=="decrease"){
								std::sort(vec.begin(),vec.end(),std::greater<double>());
							}else{
								std::sort(vec.begin(),vec.end());
							}
						}
				)
				,"push_back", ( void (dvector::*)(const double&)) & dvector::push_back
				,"pop_back", ( void (dvector::*)()) & dvector::pop_back
				,"clear", ( void (dvector::*)(void)) & dvector::clear
				,"reserve", ( void (dvector::*)(size_t)) & dvector::reserve
				,"size",[](dvector* v){
						return (int) v->size();
				}
				,sol::meta_function::index, &getter<double>
				,sol::meta_function::new_index, &setter<double,double>
				//sol::meta_function::addition, sol::resolve<std::vector<double>(const std::vector<double>&, const double&)>(::operator+),
				//sol::meta_function::addition, sol::resolve<std::vector<double>(const double&,const std::vector<double>&)>(::operator+),
				,sol::meta_function::addition, sol::overload(
						sol::resolve<dvector(const double&,const dvector&)>(::operator+),
						sol::resolve<dvector(const dvector&, const double&)>(::operator+),
						sol::resolve<dvector(const dvector&, const dvector&)>(::operator+) )
				,sol::meta_function::subtraction, sol::overload(
						sol::resolve<dvector(const dvector&, const double&)>(::operator-),
						sol::resolve<dvector(const dvector&, const dvector&)>(::operator-) )
				,sol::meta_function::multiplication, sol::overload(
						sol::resolve<dvector(const double&,const dvector&)>(::operator*),
						sol::resolve<dvector(const dvector&, const double&)>(::operator*),
						sol::resolve<dvector(const dvector&, const dvector&)>(::operator*) )
				,sol::meta_function::division, sol::overload(
						sol::resolve<dvector(const double&,const dvector&)>(::operator/),
						sol::resolve<dvector(const dvector&, const double&)>(::operator/),
						sol::resolve<dvector(const dvector&, const dvector&)>(::operator/) )
				,"min", Vector::min<double>
				,"max", Vector::max<double>

				,"abs", Vector::abs<double>
				,"sum", Vector::sum<double>
				,"log", Vector::log<double>
				,"log10", Vector::log10<double>
				,"sin", Vector::sin<double>
				,"cos", Vector::cos<double>
				,"asin", Vector::asin<double>
				,"acos", Vector::acos<double>
				,"sinh", Vector::sinh<double>
				,"cosh", Vector::cosh<double>
				,"pow", sol::resolve<dvector(const dvector&, double)>(Vector::pow<double>)

		);

		module.new_usertype<ivector>("ivector"
				,"resize", ( void (ivector::*)(size_t)) & ivector::resize
				,"add", ( void (ivector::*)(const int&)) & ivector::push_back
				,"push_back", ( void (ivector::*)(const int&)) & ivector::push_back
				,"pop_back", ( void (ivector::*)()) & ivector::pop_back
				,"clear", ( void (ivector::*)(void)) & ivector::clear
				,"reserve", ( void (ivector::*)(size_t)) & ivector::reserve
				,"size",[](ivector* v){
						return (int) v->size();
				}
				,sol::meta_function::index, &getter<int>
				,sol::meta_function::new_index, &setter<int,int>
				,sol::meta_function::addition, sol::overload(
						sol::resolve<ivector(const int&,const ivector&)>(::operator+),
						sol::resolve<ivector(const ivector&, const int&)>(::operator+),
						sol::resolve<ivector(const ivector&, const ivector&)>(::operator+) )
				,sol::meta_function::subtraction, sol::overload(
						sol::resolve<ivector(const ivector&, const int&)>(::operator-),
						sol::resolve<ivector(const ivector&, const ivector&)>(::operator-) )
				,sol::meta_function::multiplication, sol::overload(
						sol::resolve<ivector(const int&,const ivector&)>(::operator*),
						sol::resolve<ivector(const ivector&, const int&)>(::operator*),
						sol::resolve<ivector(const ivector&, const ivector&)>(::operator*) )
				,sol::meta_function::division, sol::overload(
						sol::resolve<ivector(const int&,const ivector&)>(::operator/),
						sol::resolve<ivector(const ivector&, const int&)>(::operator/),
						sol::resolve<ivector(const ivector&, const ivector&)>(::operator/) )
				,"min", Vector::min<int>
				,"max", Vector::max<int>

				,"abs", Vector::abs<int>
				,"sum", Vector::sum<int>
		);



		module.new_usertype<cvector>("cvector"
				,"resize", ( void (cvector::*)(size_t)) & cvector::resize
				,"add", ( void (cvector::*)(const std::complex<double>&)) & cvector::push_back
				,"push_back", ( void (cvector::*)(const std::complex<double>&)) & cvector::push_back
				,"pop_back", ( void (cvector::*)()) & cvector::pop_back
				,"clear", ( void (cvector::*)(void)) & cvector::clear
				,"reserve", ( void (cvector::*)(size_t)) & cvector::reserve
				,"size",[](cvector* v){
						return (int) v->size();
				}
				,sol::meta_function::length, ( size_t (cvector::*)(void)) & cvector::size
				,sol::meta_function::index, &getter<std::complex<double> >
				,sol::meta_function::new_index, sol::overload(
						&setter<std::complex<double> ,double>,
						&setter<std::complex<double> ,std::complex<double>>
				)
				,sol::meta_function::addition, sol::overload(
						sol::resolve<cvector(const double&,const cvector&)>(::operator+),
						sol::resolve<cvector(const complex&,const cvector&)>(::operator+),
						sol::resolve<cvector(const cvector&, const double&)>(::operator+),
						sol::resolve<cvector(const cvector&, const complex&)>(::operator+),
						sol::resolve<cvector(const cvector&, const cvector&)>(::operator+) )
				,sol::meta_function::subtraction, sol::overload(
						sol::resolve<cvector(const cvector&, const double&)>(::operator-),
						sol::resolve<cvector(const cvector&, const complex&)>(::operator-),
						sol::resolve<cvector(const cvector&, const cvector&)>(::operator-) )
				,sol::meta_function::multiplication, sol::overload(
						sol::resolve<cvector(const cvector&, const double&)>(::operator*),
						sol::resolve<cvector(const double&, const cvector&)>(::operator*),
						sol::resolve<cvector(const cvector&, const complex&)>(::operator*),
						sol::resolve<cvector(const cvector&, const cvector&)>(::operator*) )
				,sol::meta_function::division, sol::overload(
						sol::resolve<cvector(const cvector&, const double&)>(::operator/),
						sol::resolve<cvector(const double&, const cvector&)>(::operator/),
						sol::resolve<cvector(const cvector&, const complex&)>(::operator/),
						sol::resolve<cvector(const cvector&, const cvector&)>(::operator/) )

				,sol::meta_function::pairs, my_pairs
				,sol::meta_function::to_string,	to_string1<std::complex<double> >
				,"arg", Vector::arg<complex>
				,"real", Vector::real<complex>
				,"imag", Vector::imag<complex>
				,"phase", Vector::phase<complex>

				,"abs", Vector::abs<complex>
				,"sum", Vector::sum<complex>
				,"log", Vector::log<complex>
				,"log10", Vector::log10<complex>
				,"sin", Vector::sin<complex>
				,"cos", Vector::cos<complex>
				,"asin", Vector::asin<complex>
				,"acos", Vector::acos<complex>
				,"sinh", Vector::sinh<complex>
				,"cosh", Vector::cosh<complex>
				,"pow", sol::resolve<cvector(const cvector&, double)>( Vector::pow<complex> )

				);

		module.new_usertype<point_vector>("point_vector"
					,sol::no_constructor
					,"resize", ( void (point_vector::*)(size_t)) & point_vector::resize
					,"add", ( void (point_vector::*)(const Point3Dd&)) & point_vector::push_back
					,"push_back", ( void (point_vector::*)(const Point3Dd&)) & point_vector::push_back
					,"pop_back", ( void (point_vector::*)()) & point_vector::pop_back
					,"clear", ( void (point_vector::*)(void)) & point_vector::clear
					,"reserve", ( void (point_vector::*)(size_t)) & point_vector::reserve
					,"size",[](point_vector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<Point3Dd>
					,sol::meta_function::new_index, &setter<Point3Dd,Point3Dd>
					,sol::meta_function::length, ( size_t (point_vector::*)(void)) & point_vector::size
					//,sol::meta_function::pairs, my_pairs
					,sol::meta_function::to_string,	to_string1<Point3Dd>
			);

		module.new_usertype<point2D_vector>("point2D_vector"
					,sol::no_constructor
					,"resize", ( void (point2D_vector::*)(size_t)) & point2D_vector::resize
					,"add", ( void (point2D_vector::*)(const Point2Dd&)) & point2D_vector::push_back
					,"push_back", ( void (point2D_vector::*)(const Point2Dd&)) & point2D_vector::push_back
					,"pop_back", ( void (point2D_vector::*)()) & point2D_vector::pop_back
					,"clear", ( void (point2D_vector::*)(void)) & point2D_vector::clear
					,"reserve", ( void (point2D_vector::*)(size_t)) & point2D_vector::reserve
					,"size",[](point2D_vector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<Point2Dd>
					,sol::meta_function::new_index, &setter<Point2Dd,Point2Dd>
					,sol::meta_function::length, ( size_t (point2D_vector::*)(void)) & point2D_vector::size
					,sol::meta_function::to_string,	to_string1<Point2Dd>
			);

		module.new_usertype<pointi_vector>("point3Di_vector"
					,sol::no_constructor
					,"resize", ( void (pointi_vector::*)(size_t)) & pointi_vector::resize
					,"add", ( void (pointi_vector::*)(const Point3Di&)) & pointi_vector::push_back
					,"push_back", ( void (pointi_vector::*)(const Point3Di&)) & pointi_vector::push_back
					,"pop_back", ( void (pointi_vector::*)()) & pointi_vector::pop_back
					,"clear", ( void (pointi_vector::*)(void)) & pointi_vector::clear
					,"reserve", ( void (pointi_vector::*)(size_t)) & pointi_vector::reserve
					,"size",[](pointi_vector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<Point3Di>
					,sol::meta_function::new_index, &setter<Point3Di,Point3Di>
					,sol::meta_function::length, ( size_t (pointi_vector::*)(void)) & pointi_vector::size
					,sol::meta_function::to_string,	to_string1<Point3Di>
			);

		module.new_usertype<point2Di_vector>("point2Di_vector"
					,sol::no_constructor
					,"resize", ( void (point2Di_vector::*)(size_t)) & point2Di_vector::resize
					,"add", ( void (point2Di_vector::*)(const Point2Di&)) & point2Di_vector::push_back
					,"push_back", ( void (point2Di_vector::*)(const Point2Di&)) & point2Di_vector::push_back
					,"pop_back", ( void (point2Di_vector::*)()) & point2Di_vector::pop_back
					,"clear", ( void (point2Di_vector::*)(void)) & point2Di_vector::clear
					,"reserve", ( void (point2Di_vector::*)(size_t)) & point2Di_vector::reserve
					,"size",[](point2D_vector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<Point2Di>
					,sol::meta_function::new_index, &setter<Point2Di,Point2Di>
					,sol::meta_function::length, ( size_t (point2Di_vector::*)(void)) & point2Di_vector::size
					,sol::meta_function::to_string,	to_string1<Point2Di>
			);

		module.new_usertype<vvector>("vvector"
					,sol::no_constructor
					,"resize", ( void (vvector::*)(size_t)) & vvector::resize
					,"add", ( void (vvector::*)(const dvector&)) & vvector::push_back
					,"push_back", ( void (vvector::*)(const dvector&)) & vvector::push_back
					,"pop_back", ( void (vvector::*)()) & vvector::pop_back
					,"clear", ( void (vvector::*)(void)) & vvector::clear
					,"reserve", ( void (vvector::*)(size_t)) & vvector::reserve
					,"size",[](vvector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<dvector>
					,sol::meta_function::new_index, &setter<dvector,dvector>
					,sol::meta_function::length, ( size_t (vvector::*)(void)) & vvector::size
					//,sol::meta_function::pairs, my_pairs
					,sol::meta_function::to_string,	to_string1<dvector>
			);
		module.new_usertype<vivector>("vivector"
					,sol::no_constructor
					,"resize", ( void (vivector::*)(size_t)) & vivector::resize
					,"add", ( void (vivector::*)(const ivector&)) & vivector::push_back
					,"push_back", ( void (vivector::*)(const ivector&)) & vivector::push_back
					,"pop_back", ( void (vivector::*)()) & vivector::pop_back
					,"clear", ( void (vivector::*)(void)) & vivector::clear
					,"reserve", ( void (vivector::*)(size_t)) & vivector::reserve
					,"size",[](vivector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<ivector>
					,sol::meta_function::new_index, &setter<ivector,ivector>
					,sol::meta_function::length, ( size_t (vivector::*)(void)) & vivector::size
					//,sol::meta_function::pairs, my_pairs
					,sol::meta_function::to_string,	to_string1<ivector>
			);
		module.new_usertype<svector>("svector"
					,sol::no_constructor
					,"resize", ( void (svector::*)(size_t)) & svector::resize
					,"add", ( void (svector::*)(const std::string&)) & svector::push_back
					,"push_back", ( void (svector::*)(const std::string&)) & svector::push_back
					,"pop_back", ( void (svector::*)()) & svector::pop_back
					,"clear", ( void (svector::*)(void)) & svector::clear
					,"reserve", ( void (svector::*)(size_t)) & svector::reserve
					,"size",[](svector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<std::string>
					,sol::meta_function::new_index, &setter<std::string,std::string>
					,sol::meta_function::length, ( size_t (svector::*)(void)) & svector::size
					//,sol::meta_function::pairs, my_pairs
					,sol::meta_function::to_string,	to_string1<std::string>
			);
		module.new_usertype<bvector>("bvector"
					,sol::no_constructor
					,"resize", ( void (bvector::*)(size_t)) & bvector::resize
					,"add", ( void (bvector::*)(const bool&)) & bvector::push_back
					,"push_back", ( void (bvector::*)(const bool&)) & bvector::push_back
					,"pop_back", ( void (bvector::*)()) & bvector::pop_back
					,"clear", ( void (bvector::*)(void)) & bvector::clear
					,"reserve", ( void (bvector::*)(size_t)) & bvector::reserve
					,"size",[](bvector* v){
							return (int) v->size();
					}
					,sol::meta_function::index, &getter<bool>
					,sol::meta_function::new_index, &setter<bool,bool>
					,sol::meta_function::length, ( size_t (bvector::*)(void)) & bvector::size
					//,sol::meta_function::pairs, my_pairs
					,sol::meta_function::to_string,	to_string1<bool>
			);
	}



} // namespace my_object

// automatically bound to tostring(obj) [ __tostring ]

